/**
* Copyright (c) 2006
*      OpenAVS Developers. All Rights Reserved.
*
* Copyright (c) 2005-2006
*      NSCC. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/**     
* ! \file "vlc.c"
*   \brief Functions related to VLC decoding process.
*/

#include "vlc.h"
#include "define.h"
#include "global.h"
#include "assert.h"
#include "memory.h"

static AVS_I8 AVS_2DVLC_INTRA_dec[7][64][2] = { { {-1, -1} } };
static AVS_I8 AVS_2DVLC_INTER_dec[7][64][2] = { { {-1, -1} } };
static AVS_I8 AVS_2DVLC_CHROMA_dec[5][64][2] = { { {-1, -1} } };

static const AVS_INT EOB_Pos_intra[7] = {-1, 8, 8, 8, 6, 0, 0};
static const AVS_INT EOB_Pos_inter[7] = {-1, 2, 2, 2, 2, 0, 0};

static void  linfo_ue(AVS_INT len, AVS_INT info, AVS_INT *value1);
static void  linfo_se(AVS_INT len, AVS_INT info, AVS_INT *value1);

static void  linfo_cbp_intra(AVS_INT len,AVS_INT info,AVS_INT *cbp);
static void  linfo_cbp_inter(AVS_INT len,AVS_INT info,AVS_INT *cbp);

/**
* Function: Count the leading zero bits in data stream.
* Used in decoding Exponential-Golomb(Exp-Golomb).
*/
static AVS_INT PassLeadingZeroBits(const AVS_BYTE * buffer, AVS_DWORD * totbitoffset)
{
	AVS_LONG byteoffset;	/* byte from start of buffer */
	AVS_INT bitLeft;	/* bit from start of byte */
	AVS_INT findtag;
	AVS_INT oldbitoffset = *totbitoffset;

	// ycb
	AVS_INT data;

	findtag = 0;
	byteoffset = (*totbitoffset) / 8;
	bitLeft= 31 - ((*totbitoffset) & 7);
	data = DWORD_SWAP(*(AVS_DWORD*)(buffer + byteoffset));

	while (! findtag) {
		findtag = (data & (0x01 << bitLeft));
		bitLeft--;
		(*totbitoffset)++;
	}

	return *totbitoffset - oldbitoffset - 1;
}

/**
* Function: Read numbits from current position.
* Used in decoding fixed-/variable-length coding.
*/
AVS_INT read_bits(const AVS_BYTE * buffer, AVS_DWORD * totbitoffset, AVS_INT numbits)
{
	AVS_INT inf;
	AVS_INT byteoffset = (*totbitoffset) >> 3;
	AVS_INT bitoffset = (*totbitoffset) & 7;
	AVS_INT bitleft = 31 - bitoffset;
	AVS_UINT data = DWORD_SWAP(*(AVS_DWORD*)(buffer + byteoffset));
	AVS_UINT tmp;

	if (numbits == 0)
		return 0;

	// ycb

	*totbitoffset += numbits;
	tmp = data << bitoffset;
	inf = tmp >> (32 - numbits);
	return inf;
}

AVS_INT se(const AVS_BYTE * buffer,AVS_DWORD * totbitoffset, AVS_INT bytecount)
{
	AVS_INT info = 0;
	AVS_INT value1= 0;
	AVS_INT len = PassLeadingZeroBits(buffer, totbitoffset);
	info = read_bits(buffer, totbitoffset, len);
	linfo_se(len, info, &value1);

	return value1;
}

AVS_INT ue(const AVS_BYTE * buffer,AVS_DWORD * totbitoffset, AVS_INT bytecount)
{
	AVS_INT info = 0;
	AVS_INT value1;

	AVS_INT len = PassLeadingZeroBits(buffer, totbitoffset);
	info = read_bits(buffer, totbitoffset, len); 
	linfo_ue(len * 2 + 1, info, &value1);

	return value1;  
}

static void linfo_ue(AVS_INT len, AVS_INT info, AVS_INT *value1)
{
	AVS_INT n = 1 << (len / 2);
	info --;
	*value1 = n + info;
}

static void linfo_se(AVS_INT len, AVS_INT info, AVS_INT * value1)
{
	AVS_INT n;
	n = 1<< len;
	n += info;
	if (n & 1)
		*value1 = -(n >> 1);
	else
		*value1 = (n >> 1);
}

static void linfo_cbp_intra(AVS_INT len, AVS_INT info, AVS_INT * cbp)
{
	AVS_INT cbp_idx;
	linfo_ue(len, info, &cbp_idx);
	*cbp = NCBP[cbp_idx][0];
}

static void linfo_cbp_inter(AVS_INT len, AVS_INT info, AVS_INT * cbp)
{
	AVS_INT cbp_idx;
	linfo_ue(len, info, &cbp_idx);
	*cbp = NCBP[cbp_idx][1];
}

static void linfo_cbp_intra422(AVS_INT len, AVS_INT info, AVS_INT * cbp)
{
	AVS_INT cbp_idx;
	linfo_ue(len, info, &cbp_idx);
	*cbp = NCBP422[cbp_idx][0];
}

static void linfo_cbp_inter422(AVS_INT len, AVS_INT info, AVS_INT *cbp)
{
	AVS_INT cbp_idx;
	linfo_ue(len, info, &cbp_idx);
	*cbp = NCBP422[cbp_idx][1];
}

AVS_INT me(const AVS_BYTE* buffer,AVS_DWORD* totbitoffset, AVS_INT bytecount, AVS_DWORD dwMbType)
{

	AVS_INT cbp = 0;
	AVS_INT len = PassLeadingZeroBits(buffer, totbitoffset);
	AVS_INT info = read_bits(buffer, totbitoffset, len); 
	if (dwMbType == I_8x8) {
		/* Infra */
		linfo_cbp_intra(len * 2 + 1, info, &cbp);
	}
	else {
		/* Inter */
		linfo_cbp_inter(len * 2 + 1, info, &cbp);
	}
	return cbp;
}

AVS_INT me_chroma422(
					 const AVS_BYTE * buffer, 
					 AVS_DWORD * totbitoffset, 
					 AVS_INT bytecount, 
					 AVS_DWORD dwMbType)
{
	AVS_INT cbp = 0;
	AVS_INT len = PassLeadingZeroBits(buffer, totbitoffset);
	AVS_INT info = read_bits(buffer, totbitoffset, len); 
	if (dwMbType == I_8x8)
		linfo_cbp_intra422(len * 2 + 1, info, &cbp);
	else
		linfo_cbp_inter422(len * 2 + 1, info, &cbp);
	return cbp;
}

/**
* Function: Initialize VLC table.
*/
void MakeVlcTable()
{
	AVS_INT level, run, ipos;
	AVS_INT i;

	/* make decoder table for 2DVLC_INTRA code */

	/* Don't need to set this every time. rewrite later. */
	if (AVS_2DVLC_INTRA_dec[0][0][1] < 0) {    
		memset(AVS_2DVLC_INTRA_dec, -1, sizeof(AVS_2DVLC_INTRA_dec));
		for (i=0; i < 7; i++) {
			for (run = 0; run < 26; run++)
				for (level = 0; level < 27; level++) {
					ipos = AVS_2DVLC_INTRA[i][run][level];
					assert(ipos < 64);
					if (ipos >= 0) {
						if(i == 0) {
							AVS_2DVLC_INTRA_dec[i][ipos][0] = 
								level + 1;
							AVS_2DVLC_INTRA_dec[i][ipos][1] = 
								run;
							AVS_2DVLC_INTRA_dec[i][ipos + 1][0] = 
								-(level + 1);
							AVS_2DVLC_INTRA_dec[i][ipos + 1][1] = 
								run;
						}
						else {
							AVS_2DVLC_INTRA_dec[i][ipos][0]=level;
							AVS_2DVLC_INTRA_dec[i][ipos][1]=run;

							if (level == 0)
								continue;

							AVS_2DVLC_INTRA_dec[i][ipos + 1][0] =
								-(level);
							AVS_2DVLC_INTRA_dec[i][ipos + 1][1] =
								run;
						}
					}
				}
		}
		/* otherwise, tables are bad. */
		assert(AVS_2DVLC_INTRA_dec[0][0][1] >= 0);
	}

	/* make decoder table for 2DVLC_INTER code */

	/* Don't need to set this every time. rewrite later. */
	if (AVS_2DVLC_INTER_dec[0][0][1] < 0) {
		memset(AVS_2DVLC_INTER_dec, -1, sizeof(AVS_2DVLC_INTER_dec));
		for (i = 0; i < 7; i++) {
			for (run = 0; run < 26; run++)
				for (level = 0; level < 27;level++) {
					ipos = AVS_2DVLC_INTER[i][run][level];
					assert(ipos < 64);
					if (ipos < 0)
						continue;

					if (i == 0) {
						AVS_2DVLC_INTER_dec[i][ipos][0] = 
							level + 1;
						AVS_2DVLC_INTER_dec[i][ipos][1] = 
							run;

						AVS_2DVLC_INTER_dec[i][ipos+1][0] = 
							-(level + 1);
						AVS_2DVLC_INTER_dec[i][ipos+1][1] = 
							run;
					}
					else {
						AVS_2DVLC_INTER_dec[i][ipos][0] = 
							level;
						AVS_2DVLC_INTER_dec[i][ipos][1] = 
							run;

						if (level == 0)
							continue;

						AVS_2DVLC_INTER_dec[i][ipos+1][0] = 
							-(level);
						AVS_2DVLC_INTER_dec[i][ipos+1][1] = 
							run;
					}
				}
		}
		/* otherwise, tables are bad. */
		assert(AVS_2DVLC_INTER_dec[0][0][1] >= 0);
	}

	/* make decoder table for 2DVLC_CHROMA code */

	/* Don't need to set this every time. rewrite later. */
	if (AVS_2DVLC_CHROMA_dec[0][0][1] < 0) {
		memset(AVS_2DVLC_CHROMA_dec, -1, sizeof(AVS_2DVLC_CHROMA_dec));
		for (i = 0; i < 5; i++) {
			for (run = 0; run < 26; run++)
				for (level = 0; level < 27; level++) {
					ipos = AVS_2DVLC_CHROMA[i][run][level];
					assert(ipos < 64);
					if (ipos < 0)
						continue;

					if (i == 0) {
						AVS_2DVLC_CHROMA_dec[i][ipos][0] = 
							level + 1;
						AVS_2DVLC_CHROMA_dec[i][ipos][1] = 
							run;

						AVS_2DVLC_CHROMA_dec[i][ipos + 1][0] = 
							-(level + 1);
						AVS_2DVLC_CHROMA_dec[i][ipos + 1][1] = 
							run;
					}
					else {
						AVS_2DVLC_CHROMA_dec[i][ipos][0] = 
							level;
						AVS_2DVLC_CHROMA_dec[i][ipos][1] = 
							run;

						if (level == 0)
							continue;

						AVS_2DVLC_CHROMA_dec[i][ipos + 1][0] = -(level);
						AVS_2DVLC_CHROMA_dec[i][ipos + 1][1] = run;
					}
				}
		}
		/* otherwise, tables are bad. */
		assert(AVS_2DVLC_CHROMA_dec[0][0][1] >= 0);
	}
}

void  ReadLumaCoeff(
					const AVS_BYTE * pbCurrent,
					AVS_DWORD dwDataLen, 
					AVS_DWORD * pdwBitOffset, 
					MBINFO * pCurrMb, 
					AVS_DWORD dwPictureStructure, 
					AVS_SHORT* pResidual)
{
	static const AVS_INT incVlc_intra[7] = { 0, 1, 2, 4, 7, 10, 3000 };  
	static const AVS_INT incVlc_inter[7] = { 0, 1, 2, 3, 6,  9, 3000 };   

	AVS_INT i;
	AVS_INT ii, jj;
	AVS_DWORD tabNo = 0;
	AVS_INT symbol2D; 
	AVS_INT vlc_numcoef = 0;
	AVS_INT run = 0, level = 0;
	AVS_INT buffer_level[65];
	AVS_INT buffer_run[65];

	AVS_INT* TABNO = TabNoBuf + 512;
	AVS_INT* TABNO2 = TabNoBuf2 + 512;

	AVS_INT qp, shift;

	AVS_INT QPI, sum;
	AVS_INT ipos = -1;
	AVS_INT Golomb_se_type = SE_LUM_AC_INTER;

	AVS_INT leadingZeroBits;
	AVS_INT grad;
	AVS_INT val, val2;

	qp = pCurrMb->dwMbQp;
	memset(buffer_run, 0, 65 * sizeof(AVS_INT));
	memset(buffer_level, 0, 65 * sizeof(AVS_INT));
	if (pCurrMb->dwMbType == I_8x8) {
		for (i = 0; i < 65; i++) {
			leadingZeroBits = PassLeadingZeroBits(pbCurrent, pdwBitOffset);
			grad = VLC_Golomb_Order[0][tabNo][0]; 
			val = read_bits(pbCurrent, pdwBitOffset, leadingZeroBits + grad);
			symbol2D = (1 << (leadingZeroBits + grad)) - (1 << grad) + val; 

			if (symbol2D == EOB_Pos_intra[tabNo]) {
				vlc_numcoef = i;
				break;
			}        
			else if (symbol2D < CODE2D_ESCAPE_SYMBOL) {
				level = AVS_2DVLC_INTRA_dec[tabNo][symbol2D][0]; 
				run = AVS_2DVLC_INTRA_dec[tabNo][symbol2D][1]; 
			}
			else {
				run = (symbol2D - CODE2D_ESCAPE_SYMBOL) >> 1;
				leadingZeroBits = PassLeadingZeroBits(pbCurrent, pdwBitOffset);
				grad = 1;
				val = read_bits(pbCurrent, pdwBitOffset, leadingZeroBits+grad);
				val2 = (1 << (leadingZeroBits+grad)) - (1 << grad) + val; 
				level = val2 + ((run>MaxRun[0][tabNo])?1:RefAbsLevel[tabNo][run]);

				if (symbol2D & 1)    
					level = -level;		
			}
			buffer_level[i] = level;
			buffer_run[i] = run;
			if (abs(level) > incVlc_intra[tabNo]) {
				tabNo = TABNO[level];
			}  
		}

		for (i = (vlc_numcoef - 1); i>=0; i--) {
			ipos += (buffer_run[i] + 1);

			ii = SCAN[dwPictureStructure][ipos][0];
			jj = SCAN[dwPictureStructure][ipos][1];

			shift = IQ_SHIFT[qp];
			QPI = IQ_TAB[qp]; 
			val = buffer_level[i];
			sum = (val * QPI + (1 << (shift - 2))) >> (shift - 1);

			*(pResidual + jj * 8 + ii) =  sum;
		}
	}
	else {
		tabNo = 0;
		for (i = 0; i < 65; i++) {
			leadingZeroBits = PassLeadingZeroBits(pbCurrent, pdwBitOffset);
			grad = VLC_Golomb_Order[1][tabNo][0]; 
			val = read_bits(pbCurrent, pdwBitOffset, leadingZeroBits + grad);
			symbol2D = (1 << (leadingZeroBits + grad)) - (1 << grad) + val; 

			if (symbol2D == EOB_Pos_inter[tabNo]) {
				vlc_numcoef = i;
				break;
			}
			else if (symbol2D < CODE2D_ESCAPE_SYMBOL) {
				level = AVS_2DVLC_INTER_dec[tabNo][symbol2D][0];   
				run = AVS_2DVLC_INTER_dec[tabNo][symbol2D][1];   
			}
			else {
				run = (symbol2D - CODE2D_ESCAPE_SYMBOL) >> 1;
				leadingZeroBits = PassLeadingZeroBits(pbCurrent, pdwBitOffset);
				grad = 0;
				val = read_bits(pbCurrent, pdwBitOffset, leadingZeroBits + grad);
				val2 = (1 << (leadingZeroBits+grad)) - (1 << grad) + val; 
				level = val2 + 
					((run > MaxRun[1][tabNo]) ? 
					1 : RefAbsLevel[tabNo+7][run]);

				if (symbol2D & 1)
					level=-level;		
			}
			buffer_level[i] = level;
			buffer_run[i] = run;
			if (abs(level) > incVlc_inter[tabNo]) {
				tabNo = TABNO2[level];
			}
		}

		/* store encoded level,run to LumaCoeff[][] */
		for (i = (vlc_numcoef - 1); i >= 0; i--) {
			ipos += (buffer_run[i] + 1);

			ii = SCAN[dwPictureStructure][ipos][0];
			jj = SCAN[dwPictureStructure][ipos][1];

			shift = IQ_SHIFT[qp];
			QPI = IQ_TAB[qp];
			val = buffer_level[i];
			sum = (val * QPI + (1 << (shift - 2))) >> (shift - 1);
			*(pResidual + jj * 8 + ii) = sum;
		}
	}
}

void  ReadChromaCoeff(
					  const AVS_BYTE * pbCurrent,
					  AVS_DWORD dwDataLen, 
					  AVS_DWORD * pdwBitOffset, 
					  MBINFO * pCurrMb, 
					  AVS_DWORD dwPictureStructure, 
					  AVS_SHORT * pResidual)
{
	AVS_INT tabNo = 0;
	AVS_INT symbol2D = 0;
	AVS_INT vlc_numcoef = 0;
	AVS_INT run, level;
	AVS_INT buffer_level[65];
	AVS_INT buffer_run[65];
	static const AVS_INT EOB_Pos_chroma[2][5] = { 
		{-1, 4, 8, 6, 4}, 
		{-1, 0, 2, 0, 0} 
	};  
	static const AVS_INT incVlc_chroma[5] = {0, 1, 2, 4, 3000};   
	AVS_INT leadingZeroBits;
	AVS_INT grad;
	AVS_INT val, val2;

	/* using old style qp */
	AVS_INT qp_chroma = QP_SCALE_CR[pCurrMb->dwMbQp];
	AVS_INT temp;
	AVS_INT ii, jj;
	AVS_INT shift, QPI;
	AVS_INT ipos = -1;

	// ycb
	AVS_INT i;

	for (i = 0; i < 65; i++) {
		leadingZeroBits = PassLeadingZeroBits(pbCurrent, pdwBitOffset);
		grad = VLC_Golomb_Order[2][tabNo][0];
		val = read_bits(pbCurrent, pdwBitOffset, leadingZeroBits+grad);
		symbol2D = (1 << (leadingZeroBits + grad)) - (1 << grad) + val;

		if (symbol2D == EOB_Pos_chroma[1][tabNo]) {
			vlc_numcoef = i;
			break;
		}
		if (symbol2D < CODE2D_ESCAPE_SYMBOL) {
			level = AVS_2DVLC_CHROMA_dec[tabNo][symbol2D][0];    
			run = AVS_2DVLC_CHROMA_dec[tabNo][symbol2D][1];    
		}
		else {
			run = (symbol2D-CODE2D_ESCAPE_SYMBOL) >> 1;

			leadingZeroBits = PassLeadingZeroBits(pbCurrent, pdwBitOffset);
			grad = 0;
			val = read_bits(pbCurrent, pdwBitOffset, leadingZeroBits+grad);
			val2 = (1 << (leadingZeroBits + grad)) - (1 << grad) + val; 
			level = val2 + ((run>MaxRun[2][tabNo])?1:RefAbsLevel[tabNo+14][run]);
			if (symbol2D & 1)
				level = -level;		
		}
		buffer_level[i] = level;
		buffer_run[i] = run;

		if (abs(level) > incVlc_chroma[tabNo]) {
			if (abs(level) <= 2)
				tabNo = abs(level);
			else if (abs(level) <= 4)
				tabNo = 3;
			else
				tabNo = 4;
		}
	}

	for (i = (vlc_numcoef - 1); i >= 0; i--) {
		ipos += (buffer_run[i] + 1);

		ii = SCAN[dwPictureStructure][ipos][0];
		jj = SCAN[dwPictureStructure][ipos][1];
		shift = IQ_SHIFT[qp_chroma];
		QPI = IQ_TAB[qp_chroma]; 
		val = buffer_level[i];
		temp = (val * QPI + (1 << (shift - 2))) >> (shift - 1);

		*(pResidual + jj * 8 + ii) = temp;
	}
}

